package il.technion.cs236369.webserver;

import il.technion.cs236369.common.Common;
import il.technion.cs236369.exceptions.InvalidRequestMethod;
import il.technion.cs236369.exceptions.InvalidRequestParams;
import il.technion.cs236369.exceptions.NullEntityInPost;

import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.util.EntityUtils;

/**
 * A wrapper class for HTTP request and its properties
 * @author gil
 *
 */
public class Request {
	public enum RequestMethod {
		GET, POST, UNKNOWN
	};

	final private Socket						_socket;
	final private HttpRequest					_request;
	private String								_relativePath;
	private String								_absolutePath;
	final private DefaultHttpServerConnection	_conn;
	final private RequestMethod					_method;
	private List<NameValuePair>					_queryParams;

	/**
	 * Creates a new request object.
	 * 
	 * @param socket The socket the request was received from, and the response
	 *            should send to.
	 * @param inconn The connection the request was received with, and the
	 *            response should send with.
	 * @param hrequest The request's HttpRequest
	 * @param entity The request's entity, if available. null otherwise.
	 * @param baseDir Webserver's base dir
	 * @throws InvalidRequestParams Request's parameters parsing failed.
	 * @throws InvalidRequestMethod Request's method unknown (not POST or GET).
	 * @throws NullEntityInPost Request's method is POST, but no entity exists
	 */
	public Request(Socket socket, DefaultHttpServerConnection inconn, HttpRequest hrequest, HttpEntity entity, String baseDir)
			throws InvalidRequestParams, InvalidRequestMethod, NullEntityInPost {
		_socket = socket;
		_conn = inconn;
		_request = hrequest;
		_relativePath = generateRelativeUrl();
		_absolutePath = (new File(baseDir, _relativePath)).getPath();
		_queryParams = null;
		Common.getOut().println("Requested URL: " + _relativePath);

		//parse params
		switch (hrequest.getRequestLine().getMethod()) {
		case "GET":
			Common.getOut().println("Request Type: GET");
			_method = RequestMethod.GET;
			try {
				_queryParams = URLEncodedUtils.parse(new URI(_relativePath), "UTF-8");
			} catch (URISyntaxException e1) {
				throw new InvalidRequestParams("Failed to parse params from URI (GET)");
			}
			break;
		case "POST":
			Common.getOut().println("Request Type: POST");
			_method = RequestMethod.POST;
			try {
				if (entity == null) {
					throw new NullEntityInPost();
				}
				_queryParams = URLEncodedUtils.parse(entity);
			} catch (IOException e) {
				throw new InvalidRequestParams("Failed to parse params from entity (POST)");
			}
			break;
		default:
			Common.getOut().println("Request Type: UNKNOWN");
			_method = RequestMethod.UNKNOWN;
			throw new InvalidRequestMethod("Unknown request type (" + hrequest.getRequestLine().getMethod() + ")");
		}
		Common.getOut().println("Parsed params: " + _queryParams);
		if (entity != null) {
			try {
				EntityUtils.consume(entity);
			} catch (IOException e) {
				System.err.println("Unable to consume entity");
			}
		}
		if (_queryParams == null) {
			_queryParams = new ArrayList<>();
		}
		//format url (remove params)
		_relativePath = formatUrl(_relativePath);
		_absolutePath = formatUrl(_absolutePath);

	}

	private String generateRelativeUrl() {
		String uri = _request.getRequestLine().getUri();
		if (uri.toLowerCase().startsWith("http") == false ) {
			return uri;
		} else {
			String host = _request.getFirstHeader("Host").getValue();
			return uri.split(host)[1];
		}
		
	}

	// for testing
	public Request() {
		_socket = null;
		_absolutePath = null;
		_conn = null;
		_queryParams = null;
		_request = null;
		_method = null;
	}

	/**
	 * @return the _socket
	 */
	public Socket getSocket() {
		return _socket;
	}

	/**
	 * @return the _conn
	 */
	public DefaultHttpServerConnection getConnection() {
		return _conn;
	}

	/**
	 * @return the _request
	 */
	public HttpRequest getRequest() {
		return _request;
	}

	/**
	 * @return the parsed params
	 */
	public List<NameValuePair> getQueryParams() {
		return _queryParams;
	}

	/**
	 * @return the request's relative url
	 */
	public String getRelativePath() {
		return _relativePath;
	}

	/**
	 * @return the request's absolute url
	 */
	public String getAbsolutePath() {
		return _absolutePath;
	}

	/**
	 * @return true iff the request's method is POST
	 */
	public boolean isPostMethod() {
		if (_method == RequestMethod.POST) {
			return true;
		}
		return false;
	}
	
	private String formatUrl(String str) {
		//remove params from URL
		int index = str.indexOf("?");
		if (index >= 0) {
			return str.substring(0, index);
		}
		return str;

	}

}
